<template>
  <div class="sxForm">
    <v-contextmenu ref="vContextmenu">
      <v-contextmenu-item @click="GetCopy(formData[vContextmenuValue])">复制内容</v-contextmenu-item>
      <v-contextmenu-item @click="$refs.vContextmenu.hide()">取消操作</v-contextmenu-item>
    </v-contextmenu>
    <el-form
      ref="sxForm"
      :size="$store.getters.size"
      :model="formData"
      :rules="rules"
      :inline="inline"
      :label-position="labelPosition"
      :label-width="labelWidth"
      :label-suffix="labelSuffix"
      :hide-required-asterisk="hideRequiredAsterisk"
      :show-message="showMessage"
      :status-icon="statusIcon"
      :validate-on-rule-change="validateOnRuleChange"
      :disabled="disabled"
      :permission="permission"
    >
      <template v-for="(items, index) in formPropList">
        <el-form-item
          :key="index"
          :label="items.type === 'checkBox' ? ' ' : items.label"
          :prop="items.prop"
          :rules="items.rules"
        >
          <template v-if="inlineMessage" v-slot:error="error">
            <el-tooltip effect="dark" :content="error.error" placement="top">
              <div class="sxForm__errorMsg">
                <i class="el-icon-warning-outline" />
              </div>
            </el-tooltip>
          </template>
          <div class="contextmenu" @contextmenu.stop="vContextmenuValue = items.prop">
            <el-input
              v-contextmenu:vContextmenu
              v-if="items.type === 'input'"
              v-model="formData[items.prop]"
              :style="{
              width: items.options && items.options.width
            }"
              :type="(items.options && items.options.type) || 'text'"
              :clearable="items.options && items.options.clearable"
              :show-password="items.options && items.options.showPassword"
              :prefix-icon="items.options && items.options.prefixIcon"
              :suffix-icon="items.options && items.options.suffixIcon"
              :rows="items.options && items.options.rows"
              :autosize="items.options && items.options.autosize"
              :autocomplete="items.options && items.options.autocomplete"
              :readonly="items.options && items.options.readonly"
              :validate-event="items.options && items.options.validateEvent"
              :resize="items.options && items.options.resize"
              :placeholder="
              handlePlaceholder(items, items.placeholder || '请输入内容')
            "
              :disabled="handleDisabled(items.disabled)"
              :size="$store.getters.size"
            />
          </div>

          <div
            v-if="items.type === 'selectMore'"
            :style="{
              width: items.options && items.options.width
            }"
            class="sxForm_selectMore"
          >
            <el-input
              v-model="formData[items.prop]"
              :style="{
                width: `calc(${items.options && items.options.width} - ${
                  !handleDisabled(items.disabled) && !disabled ? '28px' : '0'
                })`
              }"
              :type="(items.options && items.options.type) || 'text'"
              :clearable="items.options && items.options.clearable"
              :show-password="items.options && items.options.showPassword"
              :prefix-icon="items.options && items.options.prefixIcon"
              :suffix-icon="items.options && items.options.suffixIcon"
              :rows="items.options && items.options.rows"
              :autosize="items.options && items.options.autosize"
              :autocomplete="items.options && items.options.autocomplete"
              :readonly="items.options && items.options.readonly"
              :validate-event="items.options && items.options.validateEvent"
              :resize="items.options && items.options.resize"
              :placeholder="
                handlePlaceholder(items, items.placeholder || '请选择内容')
              "
              :disabled="true"
              :size="$store.getters.size"
            />
            <div
              v-if="!handleDisabled(items.disabled) && !disabled"
              class="sxForm_selectMore_item"
              @click="setSelectMore(items)"
            >
              <span class="el-icon-more"></span>
            </div>
          </div>

          <el-input-number
            v-if="items.type === 'number'"
            v-model="formData[items.prop]"
            @change="blurChange(`el-input-number-${index}-${formId}`,formData[items.prop])"
            :ref="`el-input-number-${index}-${formId}`"
            :style="{
              width: items.options && items.options.width
            }"
            :class="{
              hiddenControls: items.options && items.options.controls === false
            }"
            :precision="items.options && items.options.precision"
            :controls="items.options && items.options.controls"
            :min="items.options && items.options.min"
            :max="items.options && items.options.max"
            :step="items.options && items.options.step"
            :step-strictly="items.options && items.options.stepStrictly"
            :controls-position="items.options && items.options.controlsPosition"
            :validate-event="true"
            :placeholder="
              handlePlaceholder(items, items.placeholder || '请输入数值')
            "
            :disabled="handleDisabled(items.disabled)"
            :size="$store.getters.size"
          />
          <el-select
            v-if="items.type === 'select'"
            v-model="formData[items.prop]"
            v-el-select-loadmore="items.loadMore"
            :clearable="
              items.options && items.options.clearable === false ? false : true
            "
            :style="{
              width: items.options && items.options.width
            }"
            :placeholder="
              handlePlaceholder(items, items.placeholder || '请选择')
            "
            :disabled="handleDisabled(items.disabled)"
            :value-key="items.options && items.options.valueKey"
            :allow-create="items.options && items.options.allowCreate"
            filterable
            :default-first-option="
              items.options && items.options.defaultFirstOption
            "
            :size="$store.getters.size"
            @change="
              val => {
                eventHandel('change', val, items)
              }
            "
          >
            <el-option
              v-for="(selectOption, selectIndex) in selectOptions[items.listKey]
                .list"
              :key="selectIndex"
              :label="selectOption.label"
              :value="selectOption.value"
            >
              <i
                v-if="items.options && items.options.flag"
                class="el-icon-s-flag"
                :style="'color:' + selectOption.color"
              />
            </el-option>
          </el-select>
          <el-select
            v-if="items.type === 'multipleSelect'"
            v-el-select-loadmore="items.loadMore"
            :value="formData[items.prop] ? formData[items.prop].split(',') : []"
            :style="{
              width: items.options && items.options.width
            }"
            filterable
            multiple
            :multiple-limit="items.options && items.options.multipleLimit"
            :value-key="items.options && items.options.valueKey"
            :collapse-tags="items.options && items.options.collapseTags"
            :placeholder="
              handlePlaceholder(items, items.placeholder || '请选择')
            "
            :disabled="handleDisabled(items.disabled)"
            :size="$store.getters.size"
            @change="value => $set(formData, items.prop, value.join(','))"
          >
            <el-option
              v-for="(selectOption, selectIndex) in selectOptions[items.listKey]
                .list"
              :key="selectIndex"
              :label="selectOption.label"
              :value="selectOption.value"
            />
          </el-select>
          <el-date-picker
            v-if="items.type === 'date'"
            v-model="formData[items.prop]"
            :style="{
              width: items.options && items.options.width
            }"
            :type="(items.options && items.options.type) || 'date'"
            :format="items.options && items.options.format"
            :value-format="
              (items.options && items.options.valueFormat) ||
                'yyyy-MM-dd HH:mm:ss'
            "
            :picker-options="
              (items.options && items.options.pickerOptions) || pickerOptions
            "
            :align="items.options && items.options.align"
            :default-value="items.options && items.options.defaultValue"
            :prefix-icon="items.options && items.options.prefixIcon"
            :clear-icon="items.options && items.options.clearIcon"
            :validate-event="items.options && items.options.validateEvent"
            :placeholder="
              handlePlaceholder(items, items.placeholder || '请选择日期')
            "
            :readonly="items.options && items.options.readonly"
            :disabled="handleDisabled(items.disabled)"
            :editable="items.options && items.options.editable"
            :size="$store.getters.size"
          />
          <el-date-picker
            v-if="items.type === 'dateRange'"
            v-model="formData[items.prop]"
            :style="{
              width: items.options && items.options.width
            }"
            :type="(items.options && items.options.type) || 'daterange'"
            :format="items.options && items.options.format"
            :align="items.options && items.options.align"
            :default-value="items.options && items.options.defaultValue"
            :prefix-icon="items.options && items.options.prefixIcon"
            :clear-icon="items.options && items.options.clearIcon"
            :validate-event="items.options && items.options.validateEvent"
            :value-format="
              (items.options && items.options.valueFormat) ||
                'yyyy-MM-dd HH:mm:ss'
            "
            :picker-options="
              (items.options && items.options.pickerOptions) ||
                rangePickerOptions
            "
            :range-separator="items.options && items.options.rangeSeparator"
            :disabled-date="items.disabledDate"
            :start-placeholder="items.startPlaceholder || '请选择开始日期'"
            :end-placeholder="items.endPlaceholder || '请选择结束日期'"
            :disabled="handleDisabled(items.disabled)"
            :readonly="items.options && items.options.readonly"
            :editable="items.options && items.options.editable"
            :size="$store.getters.size"
          />
          <el-checkbox
            v-if="items.type === 'checkBox'"
            v-model="formData[items.prop]"
            :style="{
              width: items.options && items.options.width
            }"
            :true-label="items.options && items.options.trueLabel"
            :false-label="items.options && items.options.falseLabel"
            :border="items.options && items.options.border"
            :disabled="handleDisabled(items.options && items.options.disabled)"
            :size="$store.getters.size"
            @change="
              val => {
                eventHandel('change', val, items)
              }
            "
          />
          <el-checkbox-group
            v-if="items.type === 'checkBoxGroup'"
            v-model="formData[items.prop]"
            :style="{
              width: items.options && items.options.width
            }"
            :min="items.options && items.options.min"
            :max="items.options && items.options.max"
            :disabled="handleDisabled(items.options && items.options.disabled)"
            :size="$store.getters.size"
          >
            <el-checkbox
              v-for="(checkBoxItem, checkBoxIndex) in items.labelList"
              :key="checkBoxIndex"
              :true-label="
                checkBoxItem.options && checkBoxItem.options.trueLabel
              "
              :false-label="
                checkBoxItem.options && checkBoxItem.options.falseLabel
              "
              :border="checkBoxItem.options && checkBoxItem.options.border"
              :label="checkBoxItem.label"
              :disabled="checkBoxItem.options && checkBoxItem.options.disabled"
              :size="$store.getters.size"
            />
          </el-checkbox-group>
          <el-switch
            v-if="items.type === 'switch'"
            v-model="formData[items.prop]"
            :style="{
              width: items.options && items.options.width
            }"
            :active-icon-class="items.options && items.options.activeIconClass"
            :inactive-icon-class="
              items.options && items.options.inactiveIconClass
            "
            :active-text="items.options && items.options.activeText"
            :inactive-text="items.options && items.options.inactiveText"
            :active-value="items.options && items.options.activeValue"
            :inactive-value="items.options && items.options.inactiveValue"
            :active-color="items.options && items.options.activeColor"
            :inactive-color="items.options && items.options.inactiveColor"
            :validate-event="items.options && items.options.validateEvent"
            :disabled="handleDisabled(items.options && items.options.disabled)"
            :label="items.label"
            :size="$store.getters.size"
            @change="
              val => {
                eventHandel('change', val, items)
              }
            "
          />
          <div
            :style="{
              width: items.options && items.options.width
            }"
            class="sxForm_equation"
            v-if="items.type === 'equation'"
          >
            <el-input
              v-model="formData[items.prop]"
              :style="{
              width: `calc(${items.options && items.options.width} - 28px)`
            }"
              :type="(items.options && items.options.type) || 'text'"
              :clearable="items.options && items.options.clearable"
              :show-password="items.options && items.options.showPassword"
              :prefix-icon="items.options && items.options.prefixIcon"
              :suffix-icon="items.options && items.options.suffixIcon"
              :rows="items.options && items.options.rows"
              :autosize="items.options && items.options.autosize"
              :autocomplete="items.options && items.options.autocomplete"
              :readonly="items.options && items.options.readonly"
              :validate-event="items.options && items.options.validateEvent"
              :resize="items.options && items.options.resize"
              :placeholder="
              handlePlaceholder(items, items.placeholder || '请输入内容')
            "
              :disabled="handleDisabled(items.disabled)"
              :size="$store.getters.size"
            />
            <div
              class="sxForm_equation_item"
              @click="setEquation(items.prop)"
              v-if="handleDisabled(items.disabled)"
            >
              <span class="el-icon-more"></span>
            </div>
          </div>
          <div
            :style="{
              width: items.options && items.options.width
            }"
            class="sxForm_group"
            v-if="items.type.indexOf('group|') != -1"
          >
            <el-input
              v-for="groupItem in GetGroup(items,'lean')"
              :key="groupItem"
              v-model="formData[`${items.prop}${groupItem}`]"
              :style="{
              width: GetGroup(items,'width')
            }"
              :type="GetGroup(items,'type')"
              :clearable="items.options && items.options.clearable"
              :show-password="items.options && items.options.showPassword"
              :prefix-icon="items.options && items.options.prefixIcon"
              :suffix-icon="items.options && items.options.suffixIcon"
              :rows="items.options && items.options.rows"
              :autosize="items.options && items.options.autosize"
              :autocomplete="items.options && items.options.autocomplete"
              :readonly="items.options && items.options.readonly"
              :validate-event="items.options && items.options.validateEvent"
              :resize="items.options && items.options.resize"
              :placeholder="
              handlePlaceholder(items, items.placeholder || '请输入内容',groupItem)
            "
              :disabled="handleDisabled(items.disabled)"
              :size="$store.getters.size"
            />
          </div>
          <template v-slot:label>
              <!-- <el-tooltip :content="items.tooltip" :disabled="!items.tooltip" :enterable="false">
                <span>{{ items.label }}</span>
              </el-tooltip> -->
              <span tabindex="-1">{{ items.label }}</span>
          </template>
          <template v-if="items.type === 'custom'" v-slot="scope">
            <slot :name="`${items.prop}Custom`" v-bind="scope" />
          </template>
        </el-form-item>
      </template>
    </el-form>
    <SxEquation
      :title="equationDialog.title"
      :width="equationDialog.width"
      :visible.sync="equationDialog.visible"
      v-bind="$attrs"
      :value="formData[equationDialog.prop]"
      @dialogConfirm="dialogConfirm"
    ></SxEquation>
  </div>
</template>

<script>
import DayJs from "dayjs";
import localStore from "@Components/assets/js/utils/localStore";
import isNil from "lodash/isNil";
import SxEquation from "../Equation";
import { generateId } from "element-ui/src/utils/util";

// TODO: pickerOptions的时间格式要和valueFormat的一致
export default {
  desc: "表单组件",
  name: "SxForm",
  components: {
    SxEquation
  },
  props: {
    permission: String,
    // 表单结构
    formProp: {
      type: Array,
      required: true
    },
    // 表单下拉框（select）的 option 值
    selectOptions: {
      type: Object,
      default() {
        return {};
      }
    },
    // 表单数据
    formData: {
      type: Object,
      default() {
        return {};
      }
    },
    // 表单验证规则
    rules: {
      type: Object,
      default() {
        return {};
      }
    },
    // 行内表单模式
    inline: {
      type: Boolean,
      default: true
    },
    // 控件的宽度
    formItemWidth: String,
    // 表单域标签的位置，如果值为 left 或者 right 时，则需要设置 label-width
    labelPosition: {
      type: String,
      default: "right"
    },
    // 表单域标签的宽度，例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto。
    labelWidth: String,
    // 表单域标签的后缀
    labelSuffix: String,
    // 是否显示必填字段的标签旁边的红色星号
    hideRequiredAsterisk: Boolean,
    // 是否显示校验错误信息
    showMessage: {
      type: Boolean,
      default: true
    },
    // 是否以行内形式展示校验信息
    inlineMessage: Boolean,
    // 是否在输入框中显示校验结果反馈图标
    statusIcon: Boolean,
    // 是否禁用该表单内的所有组件。若设置为 true，则表单内组件上的 disabled 属性不再生效
    disabled: Boolean,
    // 是否在 rules 属性改变后立即触发一次验证
    validateOnRuleChange: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      // 复制内容
      vContextmenuValue: "",
      // 编辑框
      equationDialog: {
        title: "公式编辑",
        width: "800px",
        visible: false,
        prop: null
      },
      pickerOptions: {
        shortcuts: [
          {
            text: "今天",
            onClick(picker) {
              picker.$emit(
                "pick",
                DayJs()
                  .set("h", 0)
                  .set("m", 0)
                  .set("s", 0)
                  .format("YYYY-MM-D HH:mm:ss")
              );
            }
          },
          {
            text: "7天前",
            onClick(picker) {
              picker.$emit(
                "pick",
                DayJs()
                  .subtract(7, "d")
                  .set("h", 0)
                  .set("m", 0)
                  .set("s", 0)
                  .format("YYYY-MM-D HH:mm:ss")
              );
            }
          },
          {
            text: "30天前",
            onClick(picker) {
              picker.$emit(
                "pick",
                DayJs()
                  .subtract(30, "d")
                  .set("h", 0)
                  .set("m", 0)
                  .set("s", 0)
                  .format("YYYY-MM-D HH:mm:ss")
              );
            }
          }
        ]
      },
      rangePickerOptions: {
        shortcuts: [
          {
            text: "今天",
            onClick(picker) {
              const start = DayJs()
                .set("h", 0)
                .set("m", 0)
                .set("s", 0)
                .format("YYYY-MM-D HH:mm:ss");
              const end = DayJs()
                .set("h", 23)
                .set("m", 59)
                .set("s", 59)
                .format("YYYY-MM-D HH:mm:ss");
              picker.$emit("pick", [start, end]);
            }
          },
          {
            text: "近7日",
            onClick(picker) {
              const end = DayJs()
                .set("h", 23)
                .set("m", 59)
                .set("s", 59)
                .format("YYYY-MM-D HH:mm:ss");
              const start = DayJs()
                .subtract(7, "d")
                .set("h", 0)
                .set("m", 0)
                .set("s", 0)
                .format("YYYY-MM-D HH:mm:ss");
              picker.$emit("pick", [start, end]);
            }
          },
          {
            text: "近30日",
            onClick(picker) {
              const end = DayJs()
                .set("h", 23)
                .set("m", 59)
                .set("s", 59)
                .format("YYYY-MM-D HH:mm:ss");
              const start = DayJs()
                .subtract(30, "d")
                .set("h", 0)
                .set("m", 0)
                .set("s", 0)
                .format("YYYY-MM-D HH:mm:ss");
              picker.$emit("pick", [start, end]);
            }
          },
          {
            text: "近3个月",
            onClick(picker) {
              const end = DayJs()
                .set("h", 23)
                .set("m", 59)
                .set("s", 59)
                .format("YYYY-MM-D HH:mm:ss");
              const start = DayJs()
                .subtract(3, "month")
                .set("h", 0)
                .set("m", 0)
                .set("s", 0)
                .format("YYYY-MM-D HH:mm:ss");
              picker.$emit("pick", [start, end]);
            }
          }
        ]
      }
    };
  },
  computed: {
    formId() {
      return `sx-formId-${generateId()}`;
    },
    formPropList() {
      const permissionList = localStore.getUserPermission();
      if (this.permission) {
        return this.formProp.filter(item => {
          return !permissionList.includes(
            `${this.permission}_${item.prop}`.toUpperCase()
          );
        });
      } else {
        return this.formProp;
      }
    }
  },
  methods: {
    setSelectMore(items) {
      this.$emit(`${items.prop}More`, items);
    },
    // 复制
    GetCopy(value) {
      console.log(this.vContextmenuValue);
      const input = document.createElement("input");
      input.setAttribute("value", value ?? "");
      document.body.appendChild(input);
      input.focus(); //获取焦点
      input.select(); //选中输入框
      document.execCommand("copy", true); //复制当前选中文本到前切板
      document.body.removeChild(input);
      this.$showMessage({ message: "复制成功", type: "success" });
    },
    blurChange(event, item) {
      if (this.$refs[event]) {
        this.$refs[event][0].focus();
      }
    },
    dialogConfirm(textarea) {
      this.formData[this.equationDialog.prop] = textarea;
    },
    setEquation(prop) {
      this.equationDialog.visible = !this.equationDialog.visible;
      this.equationDialog.prop = prop;
    },
    GetGroup(items, key) {
      const lean = Number(
        items.type.split("|")[2] ? items.type.split("|")[2] : 1
      );
      switch (key) {
        case "lean":
          return lean;
        case "type":
          return items.type.split("|")[1];
        case "width":
          return (
            (parseInt(items.options && items.options.width) ?? 150) / lean -
            5 +
            "px"
          );
      }
    },
    handleTest(scope) {
      console.log(scope);
    },
    handlePlaceholder(colConfig, defaultVal, index) {
      if (Array.isArray(defaultVal)) {
        const lean = this.GetGroup(colConfig, "lean");
        return defaultVal[index - 1];
      }
      if (this.handleDisabled(colConfig.disabled) || this.disabled) {
        return "";
      } else {
        return defaultVal;
      }
    },
    handleDisabled(disabled) {
      if (disabled instanceof Function) {
        return disabled();
      } else {
        return disabled;
      }
    },
    // 输入控件触发事件
    eventHandel(eventName, val, propItem) {
      if (propItem.event && propItem.event[eventName]) {
        propItem.event[eventName](val, propItem);
      }
      // 自动匹配labelKey
      if (propItem.type === "select") {
        const { labelKey, listKey } = propItem;
        if (labelKey) {
          let label = "";
          if (!isNil(val)) {
            const data = this.selectOptions[listKey].list.find(
              i => i.value === val
            );
            label = data ? data.label : "";
          }
          this.$set(this.formData, labelKey, label);
        }
      }
    },
    // 对整个表单进行重置，将所有字段值重置为初始值并移除校验结果
    resetFields() {
      this.$refs.sxForm.resetFields();
    },
    // 对整个表单进行重置，将所有字段值重置为初始值并移除校验结果
    // Function(props: array | string, callback: Function(errorMessage: string))
    validateField(props, callback) {
      this.$refs.sxForm.validateField(props, callback);
    },
    // 对整个表单进行校验的方法，参数为一个回调函数。该回调函数会在校验结束后被调用，并传入两个参数：是否校验成功和未通过校验的字段。若不传入回调函数，则会返回一个 promise
    // Function(callback: Function(boolean, object))
    validate(callback) {
      return this.$refs.sxForm.validate(callback);
    },
    // 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组，如不传则移除整个表单的校验结果
    // Function(props: array | string)
    clearValidate(props) {
      this.$refs.sxForm.clearValidate(props);
    }
  }
};
</script>

<style scoped lang="scss">
.sxForm {
  overflow-x: hidden;
  margin-top: 5px;
  &__errorMsg {
    color: #f56c6c;
    margin-left: 5px;
    display: inline-block;
    vertical-align: middle;
  }
  &_group {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
  }
  &_equation {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
    /deep/.el-input__inner {
      border-radius: 4px 0 0 4px;
      border-right: none;
    }
    &_item {
      width: 28px;
      height: 28px;
      display: flex;
      justify-content: center;
      align-items: center;
      background: #f4f4f4;
      border: 1px solid #dcdfe6;
      cursor: pointer;
      transition: 0.5s;
    }
    &_item:hover {
      border: 1px solid #1890ff;
      color: #1890ff;
    }
  }

  &_selectMore {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
    /deep/.el-input__inner {
      border-radius: 4px 0 0 4px;
      border-right: none;
    }
    &_item {
      width: 28px;
      height: 28px;
      display: flex;
      justify-content: center;
      align-items: center;
      background: #f4f4f4;
      border: 1px solid #dcdfe6;
      cursor: pointer;
      transition: 0.5s;
    }
    &_item:hover {
      border: 1px solid #1890ff;
      color: #1890ff;
    }
  }

  /deep/.hiddenControls {
    input {
      text-align: left;
    }
  }
  /deep/ .el-form-item {
    &__label {
      padding-right: 5px;
    }
  }
  /deep/ .el-input.is-disabled,
  /deep/ .el-textarea.is-disabled {
    .el-input__inner,
    .el-textarea__inner {
      color: #777;
    }
  }
  .contextmenu {
    display: inline;
  }
}
</style>
